#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <sstream>
#include <unordered_map>
#include "log.hpp"

static const std::string SEP = "\r\n";
static const std::string LINESEP = ": ";
static const std::string WWWROOT = "wwwroot";
static const std::string homepage = "test.html";
static const std::string httpversion = "HTTP/1.0";
static const std::string space = " ";
static const std::string FILESEP = ".";
static const std::string CONFIGSEP = ":";

class HttpRequest
{
public:
    HttpRequest()
        : _blank_line("\r\n"), _path(WWWROOT)
    {
    }
    void serialize()
    {
    }

    std::string path()
    {
        return _path;
    }

    void deserialize(std::string &req_str) // 反序列化
    {
        _req_line = GetOneLine(req_str);
        while (true)
        {
            std::string ret = GetOneLine(req_str);
            if (ret.empty())
            {
                break;
            }
            else if (ret == "\r\n") // 空行之后,当前req_str是请求正文
            {
                _req_text = req_str;
                break;
            }
            else // 报头
            {
                _header_line.emplace_back(ret);
            }
        }
        ParseReqLine();
        ParseHeader();
        ParseSuffix();
    }

    void print()
    {
        std::cout << "***" << _req_line << std::endl;
        for (std::string &e : _header_line)
        {
            std::cout << "---" << e << std::endl;
        }
        std::cout << std::endl;
        std::cout << _req_line << std::endl;

        std::cout << "==========" << std::endl;
        std::cout << "method" << '#' << _method << std::endl;
        std::cout << "url" << '#' << _url << std::endl;
        std::cout << "version" << '#' << _version << std::endl;
        for (auto &e : headers)
        {
            std::cout << e.first << " - " << e.second << std::endl;
        }
        std::cout << "==========" << std::endl;
    }

    std::string suffix()
    {
        return _suffix;
    }

private:
    std::string _req_line;
    std::vector<std::string> _header_line;
    std::string _blank_line;
    std::string _req_text;
    std::string _suffix; // 获取文件后缀

    std::string _method;
    std::string _url;
    std::string _path;
    std::string _version;
    std::unordered_map<std::string, std::string> headers;

    std::string GetOneLine(std::string &str)
    {
        auto pos = str.find(SEP);
        if (pos == std::string::npos)
        {
            return "";
        }
        std::string ret = str.substr(0, pos);
        str.erase(0, pos + SEP.size());
        if (ret.empty())
            return "\r\n";
        return ret;
    }

    bool ParseReqLine()
    {
        if (_req_line.empty())
            return false;
        std::stringstream ss(_req_line);
        ss >> _method >> _url >> _version;
        _path += _url;
        if (_path[_path.size() - 1] == '/')
        {
            _path += homepage;
        }
        Log(INFO, "client want to get %s\n", _path.c_str());
        return true;
    }

    void ParseHeader()
    {
        for (std::string &e : _header_line)
        {
            auto pos = e.find(LINESEP);
            if (pos == std::string::npos)
                break;
            std::string key = e.substr(0, pos);
            std::string value = e.substr(pos + LINESEP.size());
            headers[key] = value;
        }
    }

    void ParseSuffix()
    {
        auto pos = _path.rfind(FILESEP);
        if (pos == std::string::npos)
        {
            _suffix = ".html";
        }
        else
        {
            _suffix = _path.substr(pos);
        }
    }
};

class HttpResponse
{
public:
    HttpResponse() : _version(httpversion), _blank_line(SEP)
    {
    }

    void AddStatusLine(int code, std::string desc)
    {
        _code = code;
        _desc = desc; // TODO
    }

    void AddHeader(const std::string &k, const std::string &v)
    {
        _headers[k] = v;
    }

    void AddText(const std::string &text)
    {
        _resp_text = text;
    }

    std::string serialize()
    {
        _status_line = _version + space + std::to_string(_code) + space + _desc + SEP;
        for (auto &e : _headers)
        {
            _resp_header.emplace_back(e.first + LINESEP + e.second + SEP);
        }
        std::string response = _status_line;
        for (auto &e : _resp_header)
        {
            response += e;
        }
        response += _blank_line + _resp_text;
        return response;
    }

private:
    std::string _version;
    int _code;
    std::string _desc;
    std::unordered_map<std::string, std::string> _headers;
    // 应答的结构化的字段
    std::string _status_line;
    std::vector<std::string> _resp_header;
    std::string _blank_line;
    std::string _resp_text;
};

std::string GetFileContentHelper(const std::string &path, int *size)
{
    std::ifstream in(path, std::ios::binary);
    if (!in.is_open())
    {
        *size = 0;
        return "";
    }
    in.seekg(0, in.end);
    int filesize = in.tellg();
    in.seekg(0, in.beg);

    std::string content;
    content.resize(filesize);
    *size = filesize;
    in.read((char *)content.c_str(), filesize);
    in.close();
    return content;
}

class HttpServer
{
public:
    HttpServer()
    {
        GetSuffixReference();
        // _code_to_describe.insert(std::make_pair(200, "ok"));
        // _code_to_describe.insert(std::make_pair(404, "Not Found"));
        // _code_to_describe.insert(std::make_pair(301, "Move Permanently"));
        // _code_to_describe.insert(std::make_pair(302, "See Other"));
        GetCodeToDescribe();
    }
    bool GetSuffixReference()
    {
        std::ifstream in("./configuration.txt");
        if (!in.is_open())
        {
            return false;
        }
        std::string str;
        while (std::getline(in, str))
        {
            auto pos = str.find(CONFIGSEP);
            if (pos == std::string::npos)
            {
                continue;
            }
            std::string k = str.substr(0, pos);
            std::string v = str.substr(pos + CONFIGSEP.size());
            _suffix_reference.insert(std::make_pair(k, v));
        }
        in.close();
        return true;
    }
    bool GetCodeToDescribe()
    {
        std::ifstream in("./code_configuration2.txt");
        if (!in.is_open())
        {
            return false;
        }
        std::string str;
        while (std::getline(in, str))
        {
            auto pos = str.find(CONFIGSEP);
            if (pos == std::string::npos)
            {
                continue;
            }
            int k = std::stoi(str.substr(0, pos));
            std::string v = str.substr(pos + CONFIGSEP.size());
            _code_to_describe.insert(std::make_pair(k, v));
        }
        // for (auto &e : _code_to_describe)
        // {
        //     std::cout << e.first << ' ' << e.second << std::endl;
        // }
        in.close();
        return true;
    }
    std::string HanderHttpRequest(std::string req)
    {
#ifdef TEST
        std::cout << "---------------------" << std::endl;
        std::cout << req;
        std::string response = "HTTP/1.0 200 OK\r\n";
        response += "\r\n";
        response += "<html><body><h1>hello world<h1></body></html>";
        return response;
#else
        std::cout << "---------------------" << std::endl;
        std::cout<<req<<std::endl;
        std::cout << "---------------------" << std::endl;
        std::shared_ptr<HttpRequest> request = std::make_shared<HttpRequest>();
        request->deserialize(req);
        request->print();

        std::shared_ptr<HttpResponse> response = std::make_shared<HttpResponse>();
        int contentsize = 0;
        std::string text = GetFileContentHelper(request->path(), &contentsize);
        // std::cout<<request->path()<<std::endl;
        int code = 0;
        if (request->path() == "wwwroot/redirection.html")
        {
            code = 302;
            response->AddStatusLine(code, _code_to_describe[code]);
            response->AddHeader("Location", "http://110.41.38.141:8888/302.html");
            response->AddHeader("Content-Type", _suffix_reference[".html"]);
            // response->AddHeader("Location", "https://www.qq.com/");
            std::cout << response->serialize();
            return response->serialize();
        }
        else if (text.empty()) // 没有所要请求的路径就重定向到404页面
        {
            code = 404;
            text = GetFileContentHelper("./wwwroot/404.html", &contentsize);
            response->AddStatusLine(code, _code_to_describe[code]);
            response->AddHeader("Content-Type", _suffix_reference[".html"]);
        }
        else
        {
            code = 200;
            response->AddStatusLine(code, _code_to_describe[code]);
            response->AddHeader("Content-Type", _suffix_reference[request->suffix()]);
        }

        response->AddHeader("Content-Length", std::to_string(contentsize));
        response->AddText(text);
        return response->serialize();
#endif
    }

private:
    std::unordered_map<std::string, std::string> _suffix_reference;
    std::unordered_map<int, std::string> _code_to_describe;
};
